#ifndef __xBaseClass_H__
#define __xBaseClass_H__

#if !LINUX_SYSTEM
	#error only linux system supported
#endif

class uCTimer
{
public:
	static double GetTickCount()
	{
		struct timeval tm;
		struct timezone tz;
		gettimeofday(&tm, &tz);
		return (tm.tv_sec + tm.tv_usec / 1000000.0) * 1000.0;
	}
};

template<bool t_bCreateLock = true>
class xCritSecLock
{
public:
	xCritSecLock(const pthread_mutex_t* pCriSec, bool bInitialLock = true )
	{
		chASSERT(!t_bCreateLock && pCriSec != NULL);
		m_pCriSec = pCriSec;
		if(bInitialLock)
			Lock();
	}
	xCritSecLock(bool bInitialLock = true)
	{
		chASSERT(t_bCreateLock);
		m_pCriSec = new pthread_mutex_t;
		pthread_mutex_init(m_pCriSec, NULL);
		if(bInitialLock)
			Lock();
	}
	~xCritSecLock()
	{
//		chASSERT(!m_bLocked);
		if(t_bCreateLock)
		{
			if(m_bLocked)
				Unlock();
			pthread_mutex_destroy(m_pCriSec);
			delete m_pCriSec;
		}
		m_pCriSec = NULL;
	}

	void Lock()
	{
		pthread_mutex_lock(m_pCriSec);
		m_bLocked = true;
	}
	void Unlock()
	{
		chASSERT( m_bLocked );
		pthread_mutex_unlock(m_pCriSec);
		m_bLocked = false;
	}

// Implementation
private:
	pthread_mutex_t* m_pCriSec;
	bool m_bLocked;

// Private to avoid accidental use
	xCritSecLock( const xCritSecLock& ){}
	xCritSecLock& operator=( const xCritSecLock& ){return *this;}
};

#include <sys/ipc.h>
#include <sys/sem.h>

class xTmpFile
{
public:
	BOOL CreateFile(LPCTSTR lpszFileName)
	{
		chASSERT(!chIS_EMPTY(lpszFileName));
		chStrcpy<TCHAR>(m_szFileName, lpszFileName);
		
		FILE* fp = _tfopen(m_szFileName, "a+b");
		chASSERT(fp != NULL);
		fclose(fp);
		return fp != NULL;
	}
	BOOL DeleteFile()
	{
		chASSERT(m_szFileName[0] != '\0');
		unlink(m_szFileName);
		m_szFileName[0] = '\0';
	}
	LPCTSTR GetFileName()
	{
		return m_szFileName;
	}
	xTmpFile()
	{
		m_szFileName[0] = '\0';
	}
private:
	TCHAR m_szFileName[MAX_PATH];
};

template<bool t_bCreateMutex = false>
class xMutex : public xTmpFile
{
	union semun {
		 int val;                  /* value for SETVAL */
		 struct semid_ds *buf;     /* buffer for IPC_STAT, IPC_SET */
		 unsigned short *array;    /* array for GETALL, SETALL */
								   /* Linux specific part: */
		 struct seminfo *__buf;    /* buffer for IPC_INFO */
	};
public:
	BOOL CreateMutex(LPCSTR lpszName)
	{
		chASSERT(m_hMutexHandle == -1);
		TCHAR szObjName[MAX_PATH] = _T("/tmp/mutex-");
		chStrcat<TCHAR>(szObjName, lpszName);

		if(t_bCreateMutex)
		{
			xTmpFile::CreateFile(szObjName);
			key_t key = ftok(szObjName, 'm');
			chASSERT(key != -1);
			m_hMutexHandle = semget(key, 1, IPC_CREAT | 0x0666);
			chASSERT(m_hMutexHandle != -1);		

			union semun semopts;
			semopts.val = 1;
			semctl(m_hMutexHandle, 0, SETVAL, semopts);
		}
		else
		{
			key_t key = ftok(szObjName, 'm');
			chASSERT(key != -1);
			m_hMutexHandle = semget(key, 1, 0x0666);
			chASSERT(m_hMutexHandle != -1);		
		}

		SYZ_TRACE2("key = %d  m_hMutexHandle = %d", key, m_hMutexHandle);
		return m_hMutexHandle != -1;
	}

	void DestroyMutex()
	{
		SYZ_TRACE1("m_hMutexHandle = %d", m_hMutexHandle);
		chASSERT(m_hMutexHandle != -1 && !m_bLocked);
		if(t_bCreateMutex)
		{
			semctl(m_hMutexHandle, 0, IPC_RMID, 0);
			xTmpFile::DeleteFile();
		}
		m_hMutexHandle = -1;
	}

	BOOL Lock(UINT nTimeOut)
	{
		chASSERT(m_hMutexHandle != -1 && !m_bLocked);
		SYZ_TRACE1("Before Lock val = %d", semctl(m_hMutexHandle, 0, GETVAL, 0));
		BOOL bOK = FALSE;
		DWORD dwEndTime = etlGetTickCount() + nTimeOut;
		while(!bOK && (nTimeOut == -1 || etlGetTickCount() < dwEndTime))
		{
			sembuf sops = {0, -1, IPC_NOWAIT | SEM_UNDO};
			bOK = semop(m_hMutexHandle, &sops, 1) == 0;
		}
		SYZ_TRACE1("After Lock val = %d", semctl(m_hMutexHandle, 0, GETVAL, 0));
		m_bLocked = bOK;
		return bOK;
	}
	void Unlock()
	{
		chASSERT(m_hMutexHandle != -1 && m_bLocked);
		SYZ_TRACE1("Before Lock val = %d", semctl(m_hMutexHandle, 0, GETVAL, 0));
		sembuf sops = {0, 1, SEM_UNDO};
		m_bLocked = semop(m_hMutexHandle, &sops, 1) != 0;
		SYZ_TRACE1("After Lock val = %d", semctl(m_hMutexHandle, 0, GETVAL, 0));
	}
public:
	xMutex()
	{
		m_hMutexHandle = -1;
		m_bLocked = FALSE;
	}

private:
	HANDLE m_hMutexHandle;
	BOOL m_bLocked;
};

template<bool t_bCreateMap = false>
class xMapFile : public xTmpFile
{
public:
	BOOL CreateMapFile(LPCTSTR lpszFileName, DWORD dwSize)
	{
		chASSERT(m_hFileHandle == -1);
		TCHAR szObjName[MAX_PATH] = _T("/tmp/mapfile-");
		chStrcat<TCHAR>(szObjName, lpszFileName);
		if(t_bCreateMap)
		{
			xTmpFile::CreateFile(szObjName);
			key_t key = ftok(szObjName, 'S');
			chASSERT(key != -1);
			m_hFileHandle = shmget(key, dwSize, IPC_CREAT|0x0666);
			chASSERT(m_hFileHandle != -1);
		}
		else
		{
			key_t key = ftok(szObjName, 'S');
			chASSERT(key != -1);
			m_hFileHandle = shmget(key, 0, 0x0666);
			chASSERT(m_hFileHandle != -1);
		}
		return m_hFileHandle != -1;
	}

	LPVOID MapFile()
	{
		chASSERT(m_hFileHandle != -1);
		m_pMapData = (LPVOID)shmat(m_hFileHandle, 0, 0);
		return m_pMapData;
	}

	void UnmapFile()
	{
		chASSERT(m_hFileHandle != -1 && m_pMapData != NULL);
		shmdt(m_pMapData);
		m_pMapData = NULL;
	}

	void DestroyMapFile()
	{
		chASSERT(m_hFileHandle != -1 && m_pMapData == NULL);
		if(t_bCreateMap)
		{
			shmctl(m_hFileHandle, IPC_RMID, 0);
			xTmpFile::DeleteFile();
		}
		m_hFileHandle = -1;
	}
	xMapFile()
	{
		m_pMapData = NULL;
		m_hFileHandle = -1;
	}
private:
	LPVOID m_pMapData;
	HANDLE m_hFileHandle;
};

// intended for small number of simple types or pointers
template <class TKey, class TVal>
class CSimpleMap
{
public:
	TKey* m_aKey;
	TVal* m_aVal;
	int m_nSize;

// Construction/destruction
	CSimpleMap() : m_aKey(NULL), m_aVal(NULL), m_nSize(0)
	{ }

	~CSimpleMap()
	{
		RemoveAll();
	}

// Operations
	int GetSize() const
	{
		return m_nSize;
	}
	BOOL Add(TKey key, TVal val)
	{
		TKey* pKey;
		pKey = (TKey*)realloc(m_aKey, (m_nSize + 1) * sizeof(TKey));
		if(pKey == NULL)
			return FALSE;
		m_aKey = pKey;
		TVal* pVal;
		pVal = (TVal*)realloc(m_aVal, (m_nSize + 1) * sizeof(TVal));
		if(pVal == NULL)
			return FALSE;
		m_aVal = pVal;
		m_nSize++;
		SetAtIndex(m_nSize - 1, key, val);
		return TRUE;
	}
	BOOL Remove(TKey key)
	{
		int nIndex = FindKey(key);
		if(nIndex == -1)
			return FALSE;
		if(nIndex != (m_nSize - 1))
		{
			m_aKey[nIndex].~TKey();
			m_aVal[nIndex].~TVal();
			memmove((void*)&m_aKey[nIndex], (void*)&m_aKey[nIndex + 1], (m_nSize - (nIndex + 1)) * sizeof(TKey));
			memmove((void*)&m_aVal[nIndex], (void*)&m_aVal[nIndex + 1], (m_nSize - (nIndex + 1)) * sizeof(TVal));
		}
		TKey* pKey;
		pKey = (TKey*)realloc(m_aKey, (m_nSize - 1) * sizeof(TKey));
		if(pKey != NULL || m_nSize == 1)
			m_aKey = pKey;
		TVal* pVal;
		pVal = (TVal*)realloc(m_aVal, (m_nSize - 1) * sizeof(TVal));
		if(pVal != NULL || m_nSize == 1)
			m_aVal = pVal;
		m_nSize--;
		return TRUE;
	}
	void RemoveAll()
	{
		if(m_aKey != NULL)
		{
			int i;
			for(i = 0; i < m_nSize; i++)
			{
				m_aKey[i].~TKey();
				m_aVal[i].~TVal();
			}
			free(m_aKey);
			m_aKey = NULL;
		}
		if(m_aVal != NULL)
		{
			free(m_aVal);
			m_aVal = NULL;
		}

		m_nSize = 0;
	}
	BOOL SetAt(TKey key, TVal val)
	{
		int nIndex = FindKey(key);
		if(nIndex == -1)
			return FALSE;
		SetAtIndex(nIndex, key, val);
		return TRUE;
	}
	TVal Lookup(TKey key) const
	{
		int nIndex = FindKey(key);
		if(nIndex == -1)
			return NULL;    // must be able to convert
		return GetValueAt(nIndex);
	}
	TKey ReverseLookup(TVal val) const
	{
		int nIndex = FindVal(val);
		if(nIndex == -1)
			return NULL;    // must be able to convert
		return GetKeyAt(nIndex);
	}
	TKey& GetKeyAt(int nIndex) const
	{
		ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
		return m_aKey[nIndex];
	}
	TVal& GetValueAt(int nIndex) const
	{
		ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
		return m_aVal[nIndex];
	}

// Implementation

	template <typename T>
	class Wrapper
	{
	public:
		Wrapper(T& _t) : t(_t)
		{
		}
		template <typename _Ty>
		void *operator new(size_t, _Ty* p)
		{
			return p;
		}
		T t;
	};
	void SetAtIndex(int nIndex, TKey& key, TVal& val)
	{
		ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
		new(&m_aKey[nIndex]) Wrapper<TKey>(key);
		new(&m_aVal[nIndex]) Wrapper<TVal>(val);
	}
	int FindKey(TKey& key) const
	{
		int i;
		for(i = 0; i < m_nSize; i++)
		{
			if(m_aKey[i] == key)
				return i;
		}
		return -1;  // not found
	}
	int FindVal(TVal& val) const
	{
		int i;
		for(i = 0; i < m_nSize; i++)
		{
			if(m_aVal[i] == val)
				return i;
		}
		return -1;  // not found
	}
};

#endif //__xBaseClass_H__
